﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.ComponentModel;

namespace ResourceBlender_Express {
    /// <summary>
    /// A window to add and edit templates.
    /// </summary>
    public partial class WinAddTemplate : Window {
        private RBTemplate template = new RBTemplate();
        private Database db;
        private bool editing = false;

        /// <summary>
        /// Gets or sets the template being added/edited.
        /// </summary>
        /// <value>The template.</value>
        public RBTemplate FormatTemplate {
            get { return template; }
            set {
                template = value;
                DataContext = value;
                template.PropertyChanged += (s, e) => { UpdatePreview(); };
            }
        }

        /// <summary>
        /// Initializes a new <see cref="WinAddTemplate"/> to add a template.
        /// </summary>
        public WinAddTemplate() {
            InitializeComponent();
        }

        /// <summary>
        /// Initializes a new <see cref="WinAddTemplate"/> to edit a template.
        /// </summary>
        /// <param name="application">The template.</param>
        public WinAddTemplate(RBTemplate template) 
            :this() {
            this.FormatTemplate = template;
            Title = "Edit Template";
            if(template.ID >= 0) {
                editing = true;
            }
            UpdatePreview();
        }

        /// <summary>
        /// Focuses the first form field and initializes the database.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void Window_Initialized(object sender, EventArgs e) {
            this.FormatTemplate = new RBTemplate();
            db = new Database();
            txtName.Focus();
        }

        /// <summary>
        /// Checks that all required fields are filled in and valid.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private bool ValidateTemplate() {
            bool valid = true;
            txtName.GetBindingExpression(TextBox.TextProperty).UpdateSource();
            txtFileName.GetBindingExpression(TextBox.TextProperty).UpdateSource();
            txtFileLine.GetBindingExpression(TextBox.TextProperty).UpdateSource();
            if(string.IsNullOrEmpty(txtName.Text)) {
                DialogBox.Show(this, "Template name must be entered", "Error", DialogBox.DialogBoxType.OK, DialogBox.DialogBoxIcon.Error);
                txtName.Focus();
                valid = false;
            } else if(txtName.Text.Length > 255) {
                DialogBox.Show(this, string.Format("{0} must be shorter than 255 characters", "Template name"), "Error", DialogBox.DialogBoxType.OK, DialogBox.DialogBoxIcon.Error);
                txtName.Focus();
                valid = false;
            } else if(string.IsNullOrEmpty(txtFileName.Text)) {
                DialogBox.Show(this, "You must enter the format used to generate file names", "Error", DialogBox.DialogBoxType.OK, DialogBox.DialogBoxIcon.Error);
                txtFileName.Focus();
                valid = false;
            } else if(txtFileName.Text.Length > 255) {
                DialogBox.Show(this, string.Format("{0} must be shorter than 255 characters", "File name"), "Error", DialogBox.DialogBoxType.OK, DialogBox.DialogBoxIcon.Error);
                txtFileName.Focus();
                valid = false;
            } else if(string.IsNullOrEmpty(txtFileLine.Text)) {
                DialogBox.Show(this, "You must enter the format for each element", "Error", DialogBox.DialogBoxType.OK, DialogBox.DialogBoxIcon.Error);
                txtFileLine.Focus();
                valid = false;
            } else if(txtFileLine.Text.Length > 255) {
                DialogBox.Show(this, string.Format("{0} must be shorter than 255 characters", "Element format"), "Error", DialogBox.DialogBoxType.OK, DialogBox.DialogBoxIcon.Error);
                txtFileLine.Focus();
                valid = false;
            }
            return valid;
        }

        /// <summary>
        /// Inserts a template tag at the cursor position.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void InsertTag(object sender, ExecutedRoutedEventArgs e) {
            if(e.Parameter == null) {
                return;
            }
            string tagName = e.Parameter.ToString().ToLower();
            string tag = string.Empty;
            switch(e.Parameter.ToString().ToLower()) {
                case "languagecode":
                    tag = "{=LanguageCode}";
                    break;
                case "bundle":
                    tag = "{=Bundle}";
                    break;
                case "element":
                    tag = "{=Element}";
                    break;
                case "translation":
                    tag = "{=Translation}";
                    break;
            }
            int lastCaretPos = 0; // inserting text rests the caret position
            if(txtFileName.IsFocused) {
                lastCaretPos = txtFileName.CaretIndex;
                txtFileName.Text = txtFileName.Text.Insert(lastCaretPos, tag);
                txtFileName.CaretIndex = lastCaretPos + tag.Length;
            } else if(txtFileStart.IsFocused) {
                lastCaretPos = txtFileStart.CaretIndex;
                txtFileStart.Text = txtFileStart.Text.Insert(txtFileStart.CaretIndex, tag);
                txtFileStart.CaretIndex = lastCaretPos + tag.Length;
            } else if(txtFileEnd.IsFocused) {
                lastCaretPos = txtFileEnd.CaretIndex;
                txtFileEnd.Text = txtFileEnd.Text.Insert(txtFileEnd.CaretIndex, tag);
                txtFileEnd.CaretIndex = lastCaretPos + tag.Length;
            } else if(txtFileLine.IsFocused) {
                lastCaretPos = txtFileLine.CaretIndex;
                txtFileLine.Text = txtFileLine.Text.Insert(txtFileLine.CaretIndex, tag);
                txtFileLine.CaretIndex = lastCaretPos + tag.Length;
            }
        }

        /// <summary>
        /// Determines whether template tags can be inserted.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.CanExecuteRoutedEventArgs"/> instance containing the event data.</param>
        private void CanExecuteInsertTag(object sender, CanExecuteRoutedEventArgs e) {
            if(e.Parameter == null) {
                return;
            }
            switch(e.Parameter.ToString().ToLower()) {
                case "languagecode":
                case "bundle":
                    e.CanExecute = txtFileName.IsFocused || txtFileStart.IsFocused || txtFileEnd.IsFocused || txtFileLine.IsFocused;
                    break;
                case "element":
                case "translation":
                    e.CanExecute = txtFileLine.IsFocused;
                    break;
            }
        }

        /// <summary>
        /// Shows the help.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void ShowHelp(object sender, ExecutedRoutedEventArgs e) {
            Utils.OpenHelp("Templates.html");
        }

        /// <summary>
        /// Shows the larger editor.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void ShowFullEditor(object sender, ExecutedRoutedEventArgs e) {
            TextBox source = e.OriginalSource as TextBox;
            if(source == null) {
                return;
            }
            WinFullEditor editor = new WinFullEditor("Editor", source.Text);
            editor.Owner = this;
            editor.ShowDialog();
            if(editor.DialogResult == true) {
                source.Text = editor.Text;
            }
        }

        /// <summary>
        /// Saves the application to the database.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void Save(object sender, ExecutedRoutedEventArgs e) {
            if(!ValidateTemplate()) {
                return;
            }
            BackgroundWorker worker = new BackgroundWorker();
            WinProgress progress = new WinProgress("Saving template...", true);
            progress.Owner = this;
            worker.DoWork += (s, dwe) => {
                Database db = new Database();
                try {
                    db.OpenConnection();
                    using(Database.Command cmd = db.CreateCommand()) {
                        cmd.CommandText = string.Format("SELECT COUNT(id) FROM {0}templates WHERE name = @name AND id != @id", db.TablePrefix);
                        cmd.AddParameterWithValue("@name", template.Name);
                        cmd.AddParameterWithValue("@id", template.ID);
                        int numExistingTemplates = int.Parse(cmd.ExecuteScalar().ToString());
                        if(numExistingTemplates > 0) {
                            throw new Exception("Template already exists");
                        }
                        if(editing) {
                            cmd.CommandText = string.Format("UPDATE {0}templates SET name = @name, filename = @filename, filestart = @filestart, line = @line, fileend = @fileend WHERE id = @id", db.TablePrefix);
                        } else {
                            cmd.CommandText = string.Format("INSERT INTO {0}templates (name, filename, filestart, line, fileend) VALUES (@name, @filename, @filestart, @line, @fileend)", db.TablePrefix);
                        }
                        cmd.AddParameterWithValue("@filename", template.FileName);
                        cmd.AddParameterWithValue("@filestart", template.FileStart);
                        cmd.AddParameterWithValue("@line", template.FileLine);
                        cmd.AddParameterWithValue("@fileend", template.FileEnd);
                        cmd.ExecuteNonQuery();
                    }
                } catch(Exception ex) {
                    dwe.Result = ex.Message;
                } finally {
                    if(db != null && db.Connected) {
                        try {
                            db.CloseConnection();
                        } catch { }
                    }
                }
            };
            worker.RunWorkerCompleted += (s, rwe) => {
                progress.Close();
                if(rwe.Result != null) {
                    DialogBox.Show(this, "Error saving template - " + (rwe.Result ?? string.Empty).ToString(), "Error", DialogBox.DialogBoxType.OK, DialogBox.DialogBoxIcon.Error);
                } else {
                    DialogResult = true;
                    Close();
                }
            };
            progress.Show();
            worker.RunWorkerAsync();
        }

        /// <summary>
        /// Updates the template preview.
        /// </summary>
        private void UpdatePreview() {
            string[] bundleTags = new string[] { "{=LanguageCode}",
                                                  "{=Bundle}" }, // valid only for the current bundle (filename, filestart, fileend)
                     elementTags = new string[] { "{=LanguageCode}",
                                                  "{=Bundle}",
                                                  "{=Element}",
                                                  "{=Translation}"
                                                   }, // valid for each element
                     replacementBundleTags = new string[] { "en-GB",
                                                            "someBundle" },
                     replacementElementTags1 = new string[] { "en-GB", 
                                                              "someBundle", 
                                                              "element1", 
                                                              "Big long string" },
                     replacementElementTags2 = new string[] { "en-GB", 
                                                              "someBundle", 
                                                              "anotherElement", 
                                                              "Big long string\nwith a line break" };
            prvFilename.Text = Utils.FormatTemplateTags(template.FileName, bundleTags, replacementBundleTags);
            StringBuilder buffer = new StringBuilder(Utils.FormatTemplateTags(template.FileStart, bundleTags, replacementBundleTags)).Append("\n");
            buffer.AppendLine(Utils.FormatTemplateTags(template.FileLine, elementTags, replacementElementTags1))
                  .AppendLine(Utils.FormatTemplateTags(template.FileLine, elementTags, replacementElementTags2))
                  .Append(Utils.FormatTemplateTags(template.FileEnd, bundleTags, replacementBundleTags));
            prvContents.Text = buffer.ToString();
        }

        /// <summary>
        /// Closes the window.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.Input.ExecutedRoutedEventArgs"/> instance containing the event data.</param>
        private void CloseWindow(object sender, ExecutedRoutedEventArgs e) {
            Close();
        }
    }
}
